// 
// Copyright (c) 2006-2008 Ben Motmans
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Author(s):
//    Ben Motmans <ben.motmans@gmail.com>
//

using System;
using System.Collections.Generic;

namespace Anculus.Core
{
	public class BufferedAhoCorasickSearchAlgorithm : AhoCorasickSearchAlgorithm
	{
		protected Node _root;
		
		public BufferedAhoCorasickSearchAlgorithm (params string[] keywords)
		{
			CheckKeywords (keywords);
			
			_root = BuildTree (keywords);
		}
		
		public override SearchResult[] SearchAll (string text, int start, int count, params string[] keywords)
		{
			CheckArguments (text, start, count);
			if (keywords != null)
				throw new ArgumentException ("Keyword set must be passed to the constructor and cannot be changed.");

			List<SearchResult> results = null;
			if (count == int.MaxValue)
				results = new List<SearchResult> ();
			else
				results = new List<SearchResult> (count);
			
			foreach (SearchResult result in SearchIterator (text, start, count, _root)) {
				results.Add (result);
				if (results.Count == count)
					break;
			}

			return results.ToArray ();
		}

		public override SearchResult SearchFirst (string text, int start, params string[] keywords)
		{
			CheckArguments (text, start, int.MaxValue);
			if (keywords != null)
				throw new ArgumentException ("Keyword set must be passed to the constructor and cannot be changed.");
			
			IEnumerator<SearchResult> iter = SearchIterator (text, start, 1, _root).GetEnumerator ();
			if (iter.MoveNext ())
				return iter.Current;
			return SearchResult.Empty;
		}

		protected static IEnumerable<SearchResult> SearchIterator (string text, int start, int count, Node root)
		{
			if (start > 0)
				text = text.Substring (start);

			Node ptr = root;
			int index = 0;

			while (index < text.Length) {
				Node trans = null;
				
				while (trans == null) {
					trans = ptr.GetTransition (text[index]);
					
					if (ptr == root)
						break;
					
					if (trans == null)
						ptr = ptr.Failure;
				}
				
				if (trans != null)
					ptr = trans;
				
				foreach (string found in ptr.Results)
					yield return new SearchResult (index - found.Length + 1, found);

				index++;
			}
		}
	}
}
